<?php
require_once("../vendor/autoload.php");
require_once "Visitor.php";
require_once 'Class.php';
require_once 'Function.php';
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\NameResolver;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter;
use PhpParser\Node;
use PhpParser\NodeVisitor\NodeConnectingVisitor;
use PhpParser\NodeDumper;
use PhpParser\NodeFinder;
error_reporting(E_ALL^E_NOTICE^E_WARNING);

$into_func_name = "q288bq"; //存放入口方法名
$into_arg_name = "Usbpa";  //入口方法的参数名

$func_call_map = [];
$var_list = [];
$CFG = new CFG_class;
$functions = []; //存放所有的函数
$func_paths = []; //所有的函数调用可疑路径
$temp_path = []; //临时存放一条调用关系路径
$function_list = [];//存放当前函数路径，临时用
$call_info_list = [];//存放当前路径的调用信息，临时用
$temp_CFG_path = []; // 临时存放遍历CFG时的路径，防止出现死循环
$is_arrive_funcall = 0; //临时flag，遍历CFG时来判断是否到达函数调用点
$temp_vars = []; //在遍历CFG的时候临时用来存储变量，方便在dfs走到函数调用时记录当前状况
$no_dirty_var = [];
$no_dirty_vars = [];


function get_ast($filename){

    $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
    $traverser     = new NodeTraverser;
    $prettyPrinter = new PrettyPrinter\Standard;


    $traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver);
    $traverser->addVisitor(new NodeConnectingVisitor);

    try {
        $code = file_get_contents($filename);
        $stmts = $parser->parse($code);

        $stmts = $traverser->traverse($stmts);
    } catch (PhpParser\Error $e) {
        echo 'Parse Error: ', $e->getMessage();
    }
    return $stmts;
}

function deal_class($stmts){
    $traverser = new NodeTraverser;
    $traverser->addVisitor(new deal_class_Visitor);
    $stmts = $traverser->traverse($stmts);
    return $stmts;
}

function get_func_call_map($stmts){
    global $func_call_map,$functions;
    $nodeFinder = new NodeFinder;
    $functions = $nodeFinder->findInstanceOf($stmts, Node\Stmt\Function_::class); //得到所有函数定义节点组成的数组
    $methods = $nodeFinder->findInstanceOf($stmts, Node\Stmt\ClassMethod::class);
    $traverser     = new NodeTraverser;
    $traverser->addVisitor(new func_call_map_Visitor);
    $stmts = $traverser->traverse($functions);
    foreach ($functions as $key=>$value)
    {
        $functions[$value->name->name] = $value;
        $stmts = $traverser->traverse([$value]);
    }
    $traverser     = new NodeTraverser;
    $traverser->addVisitor(new method_call_map_Visitor);
    foreach ($methods as $key=>$value)
    {
        $functions[$value->name->name] = $value;
        $stmts = $traverser->traverse([$value]);
    }
    foreach ($func_call_map as $key=>$value)
    {
        foreach ($value->call_function as $key2=>$value2)
        {
            if(!$func_call_map[$key2]){
                $func_call_map[$key2] = new Func_Call_Map;
                $func_call_map[$key2]->function_name = $key2;
            }

            $func_call_map[$key2]->called_function[$key] = $value2;
        }
    }
    return $func_call_map;
}

function get_func_tag($stmts){
    global $func_call_map;
    $nodeFinder = new NodeFinder;
    $functions = $nodeFinder->findInstanceOf($stmts, Node\Stmt\Function_::class); //得到所有函数定义节点组成的数组
    $methods = $nodeFinder->findInstanceOf($stmts, Node\Stmt\ClassMethod::class);
    $traverser     = new NodeTraverser;
    $traverser->addVisitor(new sensitive_func_Visitor);
    $stmts = $traverser->traverse($functions);
    foreach ($functions as $key=>$value)
    {
        $stmts = $traverser->traverse([$value]);
    }
    $traverser     = new NodeTraverser;
    $traverser->addVisitor(new sensitive_method_Visitor);
    foreach ($methods as $key=>$value)
    {
        $stmts = $traverser->traverse([$value]);
    }
    return $func_call_map;
}

function get_CFG($function){
    global $CFG;
    $traverser = new NodeTraverser;
    $traverser->addVisitor(new get_CFG_Visitor);
    $stmts = $traverser->traverse([$function]);
    return $CFG;
}


function function_node($stmts, $function_name){
    $nodeFinder = new NodeFinder;
    $functions = $nodeFinder->find($stmts, function(Node $node, $function_name) {
        return ($node instanceof Node\Stmt\ClassMethod && $node->name->name === $function_name);
    });
    if($functions){
        return $functions[0];
    }else{
        echo "Error: not find function " . $function_name;
    }
}


function get_path($function_name){
    global $func_call_map,$func_paths,$function_list,$call_info_list, $into_func_name;
    if(in_array($function_name,$function_list) && $function_name !== end($function_list)){
        return;//进入死循环
    }elseif (strpos($function_name,$into_func_name) !== false){
        $func_path = new func_path_info;
        $func_path->function_names = $function_list;
        $func_path->infos = $call_info_list;
        $func_paths[] = $func_path;
        return;
    }else{
        foreach ($func_call_map[$function_name]->called_function as $key=>$value){
            foreach ($value as $value2){
                $call_info_list[] = $value2;
                $function_list[] = $key;
                get_path($key);
                array_pop($function_list);
                array_pop($call_info_list);
            }
        }
    }
    return;
}

function deal_block($block,$func_info,$func_name, $vars){
    global $is_arrive_funcall, $temp_vars,$no_dirty_vars,$no_dirty_var;
    $no_dirty_var = [];
    foreach ($block as $key=>$value){
        if($key === $func_info->stmt_num-1){
            foreach ($no_dirty_var as $item){
                $vars = array_flip($vars);
                unset($vars[$item]);
                $vars = array_flip($vars);
            }
            $temp_vars[] = $vars;
            continue;
        }
        if(isset($value->expr) && $value->expr->gettype() === 'Expr_Assign'){
            $vars = deal_assign($value->expr, $vars);  //下一步就是把assign处理好就行。

        }
    }
    if(isset($no_dirty_vars[0]))
        $no_dirty_vars[] = array_merge(end($no_dirty_vars),$no_dirty_var);
    else
        $no_dirty_vars[] = $no_dirty_var;
    return $vars;
}

function run_CFG($CFG, $block_num, $func_info, $func_name, $vars){
    global $temp_CFG_path, $is_arrive_funcall, $no_dirty_var,$no_dirty_vars;
    foreach ($CFG->map as $key=>$value){
        if($value->from === $block_num){
            if(in_array($value->to, $temp_CFG_path)){
                continue ;
            }
            $temp_CFG_path[] = $value->to;
            if(isset($vars[$func_name][0][0])){
                $var = array_unique(array_reduce($vars[$func_name], 'array_merge', []));
            }
            foreach (end($no_dirty_vars) as $item){
                if(in_array($item, $var)){
                    $var = array_flip($var);
                    unset($var[$item]);
                    $var = array_flip($var);
                }
            }
            $vars[$func_name][] = deal_block($CFG->blocks[$value->to],$func_info,$func_name , $var);
            $vars = run_CFG($CFG, $value->to, $func_info, $func_name, $vars);
            if($func_name === 'IMf1o0'){
                echo 123;
            }
            array_pop($vars[$func_name]);
            array_pop($temp_CFG_path);
            array_pop($no_dirty_vars);

            if($is_arrive_funcall === 1){
                $is_arrive_funcall = 0;
                break;
            }
        }
    }

    return $vars;
}


function run_path($vars){

    global $func_call_map, $temp_path, $functions, $temp_CFG_path, $is_arrive_funcall,$temp_vars;
    $func_name = array_pop($temp_path->function_names);
    $func_info = array_pop($temp_path->infos);

    if ( $func_info === NULL ){
        if($vars[$func_name] != NULL){
        }
        return $vars; //路径走到最后了,检查是否参数被污染
    }

    $function = $functions[$func_name];
    $CFG = get_CFG($function);


    

    $temp_CFG_path[] = 0;
    $is_arrive_funcall = 0;
    if(isset($vars[$func_name]))
        $var = array_unique(array_reduce($vars[$func_name], 'array_merge', []));
    $vars[$func_name][] = deal_block($CFG->blocks[0], $func_info,$func_name, $var);
    $vars = run_CFG($CFG, 0, $func_info, $func_name, $vars);
    array_pop($vars[$func_name]);
    array_pop($temp_CFG_path);
    if(count($temp_vars) !== 0){
        $vars[$func_name] = array_unique(array_reduce($temp_vars, 'array_merge', []));
        $temp_vars = [];
    }
    $into_name = end($temp_path->function_names);

    if(is_array($func_call_map[$into_name]->params)) {
        foreach ($func_call_map[$into_name]->params as $key => $value) {
            if (in_array($key, array_keys($func_info->params)) && in_array($func_info->params[$key],$vars[$func_name])) {
                $vars[$into_name][][] = $value;
            }elseif (strpos($func_info->params[$key],'|') !== false){
                if(in_array(explode("|",$func_info->params[$key])[0],Sources::$V_USERINPUT)){
                    $vars[$into_name][][] = $value;
                }
            }
        }

    }elseif($func_call_map[$into_name]->params === NULL){//非用户自定义函数，系统内置函数，基本上就是sink点了
        foreach ($func_info->params as $key=>$value){
            if(in_array(explode("|",$value)[0], Sources::$V_USERINPUT)){
                $vars[$into_name][][] = $value;
            }elseif (in_array($value, $vars[$func_name])){
                $vars[$into_name][][] = $value;
            }
        }
    }
    $vars = run_path($vars);
    return $vars;
}

function main($stmts){
    global $CFG, $func_call_map, $function_list, $func_paths, $dangrous_fun, $call_info_list, $temp_path, $into_func_name, $into_arg_name;
    foreach($func_call_map as $key=>$value){
        if(isset($dangrous_fun[$key])){
            foreach ($value->called_function as $key2=>$value2){
                foreach ($value2 as $value3){
                    if(is_array($value3->params) && array_fix(array_keys($value3->params),$dangrous_fun[$key][0])){
                        $function_list = [$key,$key2];
                        $call_info_list = [$value3];
                        get_path($key2);
                        $function_list = [];
                        $call_info_list = [];
                    }
                }
            }
        }
    }
    foreach ($func_paths as $key=>$value){   //array_pop居然会影响到全局。。。真离谱
        $sink = $value->function_names[0];
        foreach ($value->function_names as $key=>$value2){
            $path_copy[$key] = $value2;
        }

        $temp_path = $value;
        $vars = [];
        $vars[$into_func_name][] = [$into_arg_name];
        $vars = run_path($vars);
        if($vars[$sink] !== NULL) {
            echo "find vul!!!  the path is: \n";
            foreach (array_reverse($path_copy) as $func) {
                if($func === $path_copy[0]){
                    echo $func."\n";
                }else{
                    echo $func."======>";
                }
            }
        }
        $path_copy = [];
    }

}

$ast = get_ast('code.php');
$ast = deal_class($ast);

$func_call_map = get_func_call_map($ast);
$func_call_map = get_func_tag($ast);

main($ast);




